import sys
import os
import time
import datetime
import logging
from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler,FileSystemEventHandler
import shutil
import time
import tkinter as tk
from tkinter import filedialog,messagebox,simpledialog
import threading
logging.basicConfig(level=logging.INFO,
                        filename='log.txt',
                        filemode='a',  ##模式，有w和a，w就是写模式，每次都会重新写日志，覆盖之前的日志
                        format='%(asctime)s - %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S')
def writeLog(log,type):
    global show_log
    if(type == 'error'):
        logging.error(log)
    else:
        logging.info(log)
    if(type != 'stop' and type != 'sync'):
        show_log = '%s - %s\n%s'%(str(datetime.datetime.now())[:19],log,show_log)
class LoggingEventHandler(FileSystemEventHandler):
    """Logs all the events captured."""
    def __init__(self,dst_path):
        self.dst_path = dst_path
    def on_moved(self, event):
        global show_log
        super(LoggingEventHandler, self).on_moved(event)
        what = 'directory' if event.is_directory else 'file'
        logging.info("Moved %s: from %s to %s", what, event.src_path,
                     event.dest_path)
        show_log = "%s Moved %s: from %s to %s \n%s"%(str(datetime.datetime.now())[:19],what, event.src_path,event.dest_path,show_log)
    def on_created(self, event):
        global show_log
        super(LoggingEventHandler, self).on_created(event)
        file = event.src_path
        what = 'directory' if event.is_directory else 'file'
        writeLog("Created %s: %s"%(what,file),'info')
        try:
            filename = os.path.split(file)[-1]
            #判断备份目录是否存在，如果不存在则创建目录
            if(not os.path.exists(self.dst_path)):
                os.mkdir(self.dst_path)
            #移动文件，可以加第三个参数更改文件名
            # shutil.move(file,self.dst_path)
            dst_filename = os.path.join(self.dst_path,filename)
            if(os.path.exists(dst_filename)):
                dst_filename = dst_filename+'.bak'
                shutil.copy2(file,dst_filename)
            else:
                shutil.copy2(file, dst_filename)
            # 备份文件完成后记录备份时间点，修改为关闭进程是记录时间点
            # with open('timer.txt', 'w', encoding='utf8') as f:
            #     f.write(str(time.time()))
            writeLog('copy from %s to %s'%(file, dst_filename),'info')
        except Exception as e:
            if(e.args[0] == 13):
                writeLog('%s copy error , error code : %s'%(file,e.args),'error')
            else:
                writeLog('%s copy error ，error code : %s'%(file,e.args),'error')
    def on_deleted(self, event):
        global show_log
        super(LoggingEventHandler, self).on_deleted(event)
        what = 'directory' if event.is_directory else 'file'
        writeLog("Deleted %s: %s"%(what,event.src_path),'info')
    def on_modified(self, event):
        global show_log
        super(LoggingEventHandler, self).on_modified(event)
        what = 'directory' if event.is_directory else 'file'
        writeLog("Modified %s: %s"%(what, event.src_path),'info')
class DirSync:
    def __init__(self,src_path,dst_path):
        self.src_path = src_path
        self.dst_path = dst_path
        self.timer = 0
    def syncDir(self):
        global show_log
        #判断timer.txt文件中的时间点，同步该时间点之后的文件
        try:
            with open('timer.txt','r',encoding='utf8') as f:
                temp_timer = f.read().strip()
                if(temp_timer != ''):
                    self.timer = float(temp_timer)
                    #localtime中的参数必须是数值，不能是字符串，否则报错
                    timer_array = time.localtime(self.timer)
                    timer = time.strftime('%Y-%m-%d %H:%M:%S',timer_array)
                    writeLog('获取最后的备份时间点为%s'%(timer),'info')
                else:
                    writeLog('获取最后的备份时间点为空，将同步所有文件到备份目录',type='info')
        except Exception as e:
            writeLog("获取最后的备份时间点时出错，错误代码:%s"%(e.args),'error')
        src_file_list = os.listdir(self.src_path)
        total = len(src_file_list)
        bak_total = 0
        exits_total = 0
        end_backup = str(datetime.datetime.now())[:19]
        strftimer = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.timer))
        start_backup = strftimer
        writeLog("开始备份%s后创建的文件" % (strftimer), 'info')
        for src_file in src_file_list:
            filename = os.path.join(self.src_path,src_file)
            #是文件才同步
            if(os.path.isfile(filename)):
                #   os.getcwd() 方法用于返回当前工作目录
                #   os.path.getatime(file) 输出文件访问时间
                #   os.path.getctime(file) 输出文件的创建时间
                #   os.path.getmtime(file) 输出文件最近修改时间
                filecreatetime = os.path.getctime(filename)
                if(filecreatetime > self.timer):
                    dst_file_name = os.path.join(self.dst_path,os.path.split(src_file)[-1])
                    if(os.path.exists(dst_file_name)):
                        #同步的时候不同步重复文件
                        exits_total += 1
                        writeLog("file %s exists"%(filename),'sync')
                        continue
                    else:
                        try:
                            shutil.copy2(filename,self.dst_path)
                            bak_total += 1
                            writeLog('copy from: %s to: %s'%(filename, self.dst_path),'sync')
                        except Exception as e:
                            writeLog('file %s copy error,error code : %s'%(filename,e.args),'info')
            else:
                writeLog('file %s no file type or directory ,ignore'%(filename),'info')
        writeLog('file initial sync finish from:%s to:%s total:%s backup_total:%s exists_total:%s'%(start_backup,end_backup,total,bak_total,exits_total),'info')
        #同步完成后记录时间点,改为关闭程序是更新时间点
        # with open('timer.txt','w',encoding='utf8') as f:
        #     f.write(str(time.time()))
class MainFrame:
    global show_log
    def __init__(self):
        self.root = tk.Tk()
        self.root.title('文件实时备份系统')
        screenwidth,screenheight = self.root.winfo_screenwidth(),self.root.winfo_screenheight()
        width = int(screenwidth * 2/3)
        height = int(screenheight * 2/3)
        left = int((screenwidth-width)/2)
        top = int((screenheight-height)/2)
        self.root.geometry('%sx%s+%s+%s'%(width,height,left,top))
        self.root.iconbitmap('logo.ico')
        self.root.protocol('WM_DELETE_WINDOW',self.stopthread)
        self.src_path = ''
        self.dst_path = ''
        self.on_off = True
        self.observer = None
        self.src_path_entry = tk.Entry(self.root,width=65)
        self.dst_path_entry = tk.Entry(self.root,width=65)
        self.src_path_btn = tk.Button(self.root,command=self.getSrcPath,width=15,text='选择源目录')
        self.dst_path_btn = tk.Button(self.root,command=self.getDstPath,width=15,text='选择目标目录')
        self.run_btn = tk.Button(self.root,command=self.runthread,width=15,text='开始备份',bg='green')
        self.stop_btn = tk.Button(self.root,command=self.stopthread,width=15,text='停止备份',bg='red')
        self.src_path_entry.grid(row=0, column=0, padx=20, pady=10)
        self.dst_path_entry.grid(row=1, column=0, padx=20, pady=10)
        self.src_path_btn.grid(row=0, column=1)
        self.dst_path_btn.grid(row=1, column=1)
        self.timer_btn = tk.Button(self.root,command=self.setTimer,width=15,text='设置同步时间点',bg='gray')

        self.run_btn.grid(row=0, column=2)
        self.stop_btn.grid(row=1, column=2)
        self.timer_btn.grid(row=2, column=2, columnspan=3,padx=20,pady=5)
        self.frame_bottom = tk.Frame(self.root)
        self.frame_bottom.grid(row=3,column=0,columnspan=3)
        self.log_scroll = tk.Scrollbar(self.frame_bottom)
        self.log = tk.Text(self.frame_bottom, width=100,yscrollcommand=self.log_scroll.set)
        self.log_scroll.pack(side=tk.RIGHT,fill=tk.Y)
        self.log.pack(padx=30,pady=10,expand='yes',fill=tk.BOTH)
        self.log.insert(1.0, '%s先选择备份源目录和目标目录，再点击开始按钮 \n' % str(datetime.datetime.now())[:19])
        self.log_scroll.config(command=self.log.yview())
        self.root.mainloop()
    def getSrcPath(self):
        self.src_path = filedialog.askdirectory()
        self.src_path_entry.delete(0,tk.END)
        self.src_path_entry.insert(0,self.src_path)
    def getDstPath(self):
        self.dst_path = filedialog.askdirectory()
        self.dst_path_entry.delete(0, tk.END)
        self.dst_path_entry.insert(0, self.dst_path)
    def setTimer(self):
        timer = simpledialog.askstring('设置同步文件时间点',
                                       '正确格式：1970-01-01 08:00:00')
        if(timer):
            try:
                time_array = time.strptime(timer,'%Y-%m-%d %H:%M:%S')
                temp_timer = time.mktime(time_array)
                with open('timer','w') as f:
                    f.write(str(temp_timer))
                writeLog('设置起始备份时间点为：%s'%(timer), 'info')
            except Exception as e:
                messagebox.showerror('日期输入有误','正确格式：1970-01-01 08:00:00',)
                writeLog('设置起始备份时间点出错，请填写正确的时间','error')
    def run(self):
        global show_log
        # 实时监控并复制新创建的文件
        event_handler = LoggingEventHandler(dst_path=self.dst_path)
        self.observer = Observer()
        self.observer.schedule(event_handler, self.src_path, recursive=True)
        self.observer.start()
        #同步已有文件
        DirSync(self.src_path, self.dst_path).syncDir()
        try:
            while self.on_off:
                time.sleep(1)
                if (show_log):
                    self.log.insert(1.0, show_log)
                    show_log = ''
        except KeyboardInterrupt:
            self.observer.stop()
        self.observer.join()
    def runthread(self):
        self.src_path = self.src_path_entry.get()
        self.dst_path = self.dst_path_entry.get()
        if( self.src_path and self.dst_path ):
            if(self.src_path != self.dst_path):
                self.t = threading.Thread(target=self.run)
                self.run_btn.config(state='disable')
                self.log.insert(1.0, '%s 程序已开始自动备份，直到窗口被关闭 \n' % str(datetime.datetime.now())[:19])
                self.t.start()
            else:
                self.log.insert(1.0, '%s 源目录不能和目标目录重复! \n' % str(datetime.datetime.now())[:19])
                return
        else:
            self.log.insert(1.0, '%s 请先选择正确的文件目录再点击开始 \n' % str(datetime.datetime.now())[:19])
    def stopthread(self):
        if(messagebox.askokcancel('确认停止备份','确认停止备份吗？')):
            with open('timer.txt','w') as f:
                f.write(str(time.time()))
            # 备份进程停止前记录停止日志
            writeLog('stop backup', 'stop')
            if (self.on_off):
                self.on_off = False
            if (self.observer):
                self.observer.stop()
            self.root.quit()
if __name__ == "__main__":
    show_log = ''
    app = MainFrame()
